package cn.com.easy.pay.weixinpay.controller.publicnumber;

import java.io.UnsupportedEncodingException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;

import cn.com.easy.exception.BusinessException;
import cn.com.easy.pay.weixinpay.config.WeixinPayConfig;
import cn.com.easy.pay.weixinpay.param.UnifiedOrderReturnParam;
import cn.com.easy.pay.weixinpay.param.UnifiedOrderUserParam;
import cn.com.easy.pay.weixinpay.utils.Signature;
import cn.com.easy.pay.weixinpay.utils.WeixinPayUnifiedOrderUtils;
import cn.com.easy.utils.ResponseOutputUtils;

import com.google.common.collect.Maps;

/**
 * 微信h5发起支付,生成js脚本控制器
 * 
 * @author nibili 2017年5月11日
 * 
 */
public abstract class WeixinPayH5RequestJSBaseController {

	private Logger logger = LoggerFactory.getLogger(WeixinPayH5RequestJSBaseController.class);

	/**
	 * 如果只想跳转，而不想要返回着路页，返回true;
	 * 
	 * @return
	 * @author nibili 2017年5月11日
	 */
	public abstract boolean isJustJs(HttpServletRequest request);

	/**
	 * 统一下单成功回调
	 * 
	 * @param request
	 * @param unifiedOrderUserParam
	 * @param unifiedOrderReturnParam
	 * @param jsString
	 *            支付脚本
	 * @return 要跳转的页面
	 * @throws BusinessException
	 * @author nibili 2017年5月11日
	 */
	public abstract String successPage(HttpServletRequest request, UnifiedOrderUserParam unifiedOrderUserParam, UnifiedOrderReturnParam unifiedOrderReturnParam, String jsString)
			throws BusinessException;

	/**
	 * 统一下单异常回调
	 * 
	 * @param request
	 * @param unifiedOrderUserParam
	 * @param unifiedOrderReturnParam
	 * @param errorString
	 * @return
	 * @throws BusinessException
	 * @author nibili 2017年5月11日
	 */
	public abstract String errorPage(HttpServletRequest request, UnifiedOrderUserParam unifiedOrderUserParam, UnifiedOrderReturnParam unifiedOrderReturnParam, String errorString);

	/**
	 * 获取统一下单参数
	 * 
	 * @param request
	 * @return
	 * @author nibili 2017年5月8日
	 */
	public abstract UnifiedOrderUserParam getUnifiedOrderUserParam(HttpServletRequest request) throws BusinessException;

	/**
	 * 获取微信支付配置
	 * 
	 * @param httpRequest
	 * @return
	 * @author nibili 2017年5月8日
	 */
	public abstract WeixinPayConfig getWeixinPayConfig(HttpServletRequest httpRequest) throws BusinessException;

	/**
	 * 会行进行统一下单请求，然后组装支付请求JS<br/>
	 * 例：<script></script>
	 * 
	 * @param request
	 * @param httpResponse
	 * @return
	 * @throws Exception
	 * @author nibili 2017年5月11日
	 */
	@RequestMapping("/unifiedorder")
	public String unifiedorderAndRequestJS(HttpServletRequest request, HttpServletResponse response) {

		try {
			// 微信支付配置
			WeixinPayConfig weixinPayConfig = this.getWeixinPayConfig(request);
			// 用户定义的下单参数
			UnifiedOrderUserParam unifiedOrderUserParam = this.getUnifiedOrderUserParam(request);
			if (StringUtils.isBlank(unifiedOrderUserParam.getOpenid()) == true) {
				throw new BusinessException("openId不能为空");
			}
			// 统一下单 请求
			UnifiedOrderReturnParam unifiedOrderReturnParam = WeixinPayUnifiedOrderUtils.doUnifiedOrder(unifiedOrderUserParam, weixinPayConfig);
			// 判断下单是否成功,SUCCESS/FAIL
			if (StringUtils.equals(unifiedOrderReturnParam.getReturn_code(), "SUCCESS") == true) {
				// 成功
				String jsString = getPayJSString(weixinPayConfig.getAppid(), unifiedOrderReturnParam.getPrepay_id(), weixinPayConfig.getSign_type(), weixinPayConfig.getAppkey());
				if (this.isJustJs(request) == true) {
					ResponseOutputUtils.renderHtml(response, jsString);
					return "";
				} else {
					return successPage(request, unifiedOrderUserParam, unifiedOrderReturnParam, jsString);
				}
			} else {
				// 错误
				return errorPage(request, unifiedOrderUserParam, unifiedOrderReturnParam, unifiedOrderReturnParam.getReturn_msg());
			}
		} catch (BusinessException ex) {
			logger.error("微信统一下单发生异常(自定义异常):" + ex.getMessage(), ex);
			// 错误
			return errorPage(request, null, null, ex.getMessage());
		} catch (Exception ex) {
			logger.error("微信统一下单发生异常:" + ex.getMessage(), ex);
			// 错误
			return errorPage(request, null, null, ex.getMessage());
		}
	}

	/**
	 * 获取支付js脚本
	 * 
	 * @param appId
	 * @param prepayId
	 * @param signType
	 * @param appKey
	 * @return
	 * @author nibili 2017年5月11日
	 * @throws UnsupportedEncodingException
	 * @throws IllegalAccessException
	 */
	private String getPayJSString(String appId, String prepayId, String signType, String appKey) throws IllegalAccessException, UnsupportedEncodingException {
		// 时间戳，自1970年以来的秒数
		String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
		// 随机串
		String nonceStr = RandomStringUtils.random(16, true, true);

		/**
		 * 脚本所需参数<br/>
		 * 详参:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index
		 * =6
		 * */
		Map<String, String> param = Maps.newTreeMap();
		param.put("appId", appId);
		param.put("timeStamp", timeStamp);
		param.put("nonceStr", nonceStr);
		param.put("package", "prepay_id=" + prepayId);
		param.put("signType", signType);
		String paySign = Signature.getSign(param, signType, appKey);
		StringBuilder sb = new StringBuilder();
		sb.append("<script type=\"text/javascript\">");
		sb.append("function onBridgeReady(){");
		sb.append("  WeixinJSBridge.invoke(");
		sb.append(" 'getBrandWCPayRequest', {");
		sb.append("  \"appId\":\"" + appId + "\","); // 公众号名称，由商户传入
		sb.append(" \"timeStamp\":\"" + timeStamp + "\", ");
		sb.append(" \"nonceStr\":\"" + nonceStr + "\",");
		sb.append(" \"package\":\"prepay_id=" + prepayId + "\", ");
		sb.append(" \"signType\":\"" + signType + "\","); // 微信签名方式：
		sb.append(" \"paySign\":\"" + paySign + "\""); // 微信签名
		sb.append(" },");
		sb.append(" function(res){");
		sb.append(" if(res.err_msg == \"get_brand_wcpay_request:ok\" ) {}"); // 使用以上方式判断前端返回,微信团队郑重提示：res.err_msg将在用户支付成功后返回
																				// ok，但并不保证它绝对可靠。
		sb.append(" }");
		sb.append(" ); ");
		sb.append("	}");
		sb.append("if (typeof WeixinJSBridge == \"undefined\"){");
		sb.append(" if( document.addEventListener ){");
		sb.append("  document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);");
		sb.append("  }else if (document.attachEvent){");
		sb.append(" document.attachEvent('WeixinJSBridgeReady', onBridgeReady); ");
		sb.append("  document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);");
		sb.append("  }");
		sb.append("	}else{");
		sb.append("onBridgeReady();");
		sb.append("}");
		sb.append("</script>");
		return sb.toString();
	}
}
